/*
 * Copyright (C) 2010 Yury Kudryashov.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package name.kudryashov.functions;

import name.kudryashov.base.Argument;

public class ComposableFunction<A, R> implements Function<A, R> {
    private final Function<A, R> f;

    ComposableFunction(Function<A, R> f) {
        this.f = Argument.requireNotNull(f, "f");
    }

    public <B> ComposableFunction<A, B> andThen(final Function<? super R, ? extends B> f2) {
        Argument.requireNotNull(f2, "f2");
        return new ComposableFunction<A, B>(new Function<A, B>() {
            public B apply(A arg) {
                return f2.apply(f.apply(arg));
            }

            public String toString() {
                return f2 + "(" + f + ")";
            }
        });
    }

    public R apply(A arg) {
        return f.apply(arg);
    }

    public Predicate<A> andThen(final Predicate<? super R> f2) {
        Argument.requireNotNull(f2, "f2");
        return new Predicate<A>() {
            public boolean apply(A arg) {
                return f2.apply(f.apply(arg));
            }

            public String toString() {
                return f2 + "(" + f + ")";                
            }
        };
    }

    public Procedure<A> andThen(final Procedure<? super R> f2) {
        Argument.requireNotNull(f2, "f2");
        return new Procedure<A>() {
            public void call(A arg) {
                f2.call(f.apply(arg));
            }

            public String toString() {
                return f2 + "(" + f + ")";
            }
        };

    }

    public Generator<R> bind(final A arg) {
        return new Generator<R>() {
            public R get() {
                return f.apply(arg);
            }

            public String toString() {
                return f + "(" + arg + ")";
            }
        };
    }

    public Procedure<A> ignoreResult() {
        return new Procedure<A>() {
            public void call(A arg) {
                f.apply(arg);
            }

            @Override
            public String toString() {
                return f + "[ignoring result]";
            }
        };
    }
}
